#include "mcvbasicarrays.h"
#include "mcvdatum.h"

#include <assert.h>

using namespace MCV;
#define MCVBASECOLLECTION 8712

// inherited stuff
int BaseCollection::type()
{
	return MCVARRAY;
}

int BaseCollection::rawType()
{
	return MCVBASECOLLECTION;
}

int BaseCollection::subType()
{
	return -1;
}

int BaseCollection::getId()
{
	return _id;
}

void BaseCollection::setId(int newId)
{
	_id = newId;
}

int BaseCollection::numElements()
{
	// eh
	return -1;
}

int BaseCollection::numDims()
{
	return _numDims;
}

int BaseCollection::size(int d)
{
	return _vdims[d];
}

std::vector<int> BaseCollection::size()
{
	return _vdims;
}

int BaseCollection::stype1d(int x)
{
	return _types[x];
}

int BaseCollection::stypeNd(int* coords, int N)
{
	return _types[pNd(coords, N)];
}

inline Datum BaseCollection::asDatum(int x)
{
	switch(_types[x])
	{
	case MCVINT:
		return Datum(*((mcvint*)(_data[x])));
	case MCVFLOAT:
		return Datum(*((mcvfloat*)(_data[x])));
	case MCVUCHAR:
		return Datum(*((mcvuchar*)(_data[x])));
	case MCVARRAY:
		return Datum((Array*)(_data[x]));
	default:
		return Datum(-1);
	}
}

Datum BaseCollection::get1d(int x)
{
	return asDatum(x);
}

Datum BaseCollection::get2d(int x, int y)
{
	return asDatum(x + (y * _cumDims[1]));
}

Datum BaseCollection::get3d(int x, int y, int z)
{
	return asDatum(x + (y * _cumDims[1]) + (z * _cumDims[2]));
}

Datum BaseCollection::getNd(int* coords, int N)
{
	int pos = 0;
	for(int i = 0; i < N; ++i)
		pos += (coords[i] * _cumDims[i]);
	return asDatum(pos);
}

void BaseCollection::incrRefCount()
{
	++_refcount;
}

void BaseCollection::decrRefCount()
{
	--_refcount;
	if(_refcount == 0)
		if(_mm != NULL)
			_mm->remove(this);
		else
			delete this;
}

int BaseCollection::refCount()
{
	return _refcount;
}

bool BaseCollection::soft()
{
	return _soft;
}

const std::string BaseCollection::getEncoderType()
{
	// eh?
	return "GENR";
}

void BaseCollection::setmm(MemoryManager* mm)
{
	_mm = mm;
}

// constructors and whatnot
BaseCollection::BaseCollection(int numdims, int* dimsizes)
{
	_numDims = numdims;
	for(int i = 0; i < numdims; ++i)
		_vdims.push_back(dimsizes[i]);
	initBuffers();
}

BaseCollection::BaseCollection(std::vector<int> dims)
{
	_vdims = dims;
	_numDims = dims.size();
	initBuffers();
}

void BaseCollection::initBuffers()
{
	_cumDims = new int[_numDims + 1];
	_soft = false;
	_id = -1;
	_refcount = 0;
	_mm = NULL;
	int prod = 1;
	_cumDims[0] = prod;
	for(int i = 1; i <= _numDims; ++i)
	{
		prod *= _vdims[i-1];
		_cumDims[i] = prod;
	}

	int numel = _cumDims[_numDims];
	_data = new void*[numel];
	_types = new int[numel];

	for(int i = 0; i < numel; ++i)
	{
		_data[i] = NULL;
		_types[i] = MCVARRAY;
	}
}

BaseCollection::~BaseCollection()
{
	int numel = _cumDims[_numDims];
	for(int i = 0; i < numel; ++i)
		unset(i);
	delete[] _data;
	delete[] _types;
	delete[] _cumDims;
}

void BaseCollection::harden()
{
	_soft = true;
}

int BaseCollection::p2d(int x, int y)
{
	return x + y * _cumDims[1];
}

int BaseCollection::p3d(int x, int y, int z)
{
	return x + (y * _cumDims[1]) + (z * _cumDims[2]);
}

int BaseCollection::pNd(int* coords, int N)
{
	int sum = 0;
	for(int i = 0; i < N; ++i)
		sum += (coords[i] * _cumDims[i]);
	return sum;
}

void BaseCollection::seti(int p, mcvint val)
{
	unset(p);
	_data[p] = (void*)(new mcvint(val));
	_types[p] = MCVINT;
}

void BaseCollection::setf(int p, mcvfloat val)
{
	unset(p);
	_data[p] = (void*)(new mcvfloat(val));
	_types[p] = MCVFLOAT;
}

void BaseCollection::setu(int p, mcvuchar val)
{
	unset(p);
	_data[p] = (void*)(new mcvuchar(val));
	_types[p] = MCVUCHAR;
}

void BaseCollection::sets(int p, std::string val)
{
	unset(p);
	_data[p] = (void*)(new std::string(val));
	_types[p] = MCVSTRING;
}

void BaseCollection::setd(int p, Datum& val)
{
	unset(p);

	switch(val.type())
	{
	case MCVINT:
		seti(p, (mcvint)val);
		break;
	case MCVFLOAT:
		setf(p, (mcvfloat)val);
		break;
	case MCVUCHAR:
		setu(p, (mcvuchar)val);
		break;
	case MCVSTRING:
		sets(p, "blargh");
		break;
	case MCVARRAY:
		Array* av = (Array*)val;
		assert(av == NULL || av->soft());
		_types[p] = MCVARRAY;
		_data[p] = (void*)av;
		if(av)
			av->incrRefCount();
		//_subrefs.push_back(val);
		break;
	}
}

bool BaseCollection::isFlat()
{
	return false;
}

FlatArrayInfo* BaseCollection::getFlatArray()
{
	return NULL;
}

void BaseCollection::unset(int p)
{
	if(_data[p] == NULL)
		return;

	assert(_soft);

	switch(_types[p])
	{
	case MCVINT:
		delete (mcvint*)(_data[p]);
		break;
	case MCVFLOAT:
		delete (mcvfloat*)(_data[p]);
		break;
	case MCVUCHAR:
		delete (mcvuchar*)(_data[p]);
		break;
	case MCVSTRING:
		delete (std::string*)(_data[p]);
		break;
	case MCVARRAY:
		((Array*)(_data[p]))->decrRefCount();
		break;
	default:
		break;
	}
	_data[p] = NULL;
}

mcvfloat BaseCollection::tofloat(int p)
{
	if(_types[p] == MCVFLOAT)
		return *((mcvfloat*)_data[p]);
	else if(_types[p] == MCVINT)
		return (mcvfloat)(*((mcvint*)_data[p]));
	else if(_types[p] == MCVUCHAR)
		return (mcvfloat)(*((mcvuchar*)_data[p]));
	else
		return (mcvfloat)(-1.0);
}

mcvint BaseCollection::toint(int p)
{
	if(_types[p] == MCVINT)
		return *((mcvint*)_data[p]);
	else if(_types[p] == MCVFLOAT)
		return (mcvint)(*((mcvfloat*)_data[p]));
	else if(_types[p] == MCVUCHAR)
		return (mcvint)(*((mcvuchar*)_data[p]));
	else
		return (mcvint)(-1);
}

mcvuchar BaseCollection::touchar(int p)
{
	if(_types[p] == MCVUCHAR)
		return *((mcvuchar*)_data[p]);
	else if(_types[p] == MCVFLOAT)
		return (mcvuchar)(*((mcvfloat*)_data[p]));
	else if(_types[p] == MCVINT)
		return (mcvuchar)(*((mcvint*)_data[p]));
	else
		return (mcvuchar)(0);
}

std::string BaseCollection::tosstring(int p)
{
	if(_types[p] == MCVSTRING)
		return *((std::string*)_data[p]);
	else
		return "INV.CONV.";
}